#include "logger.h"
#include <bits/types/time_t.h>
#include <cerrno>
#include <cstdarg>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <ctime>
#include <new>
#include <sstream>
#include <stdexcept>
#include <chrono>
#include <iostream>
#include <iomanip>
#include <string.h>
#include <string>
// #include <unistd.h>

//初始化数组
const char* Logger::s_level[] = {
#define XX(name) #name,
    LOG_LEVEL(XX) 
#undef XX
};

Logger* Logger::m_logInstance = nullptr;

Logger::Logger()
:m_level(Logger::DEBUG)
,m_fileName("log.txt")
,m_fout(NULL) 
,m_maxSize(UINT64_MAX)
,m_len(0)
{}

Logger::~Logger()
{
    close();
}

void Logger::getNowTime(std::string& time)
{
    auto now = std::chrono::system_clock::now();
    auto timet = std::chrono::system_clock::to_time_t(now);
    // std::cout << "timet: " << timet << std::endl; //timet: 1637235226
    auto localTime = *std::gmtime(&timet);
    auto ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now).time_since_epoch().count() % 1000;
    // std::cout << "ms: " << ms << std::endl; //ms: 632
    std::stringstream ss; 
    ss << std::put_time(&localTime, "%Y-%m-%dT%H:%M:%S") << "." << std::setfill('0') << std::setw(3) << ms;
    ss >> time;
}

void Logger::getNowTime(char* timeStamp, uint32_t size)
{
    memset(timeStamp, 0, size);
    time_t ticks = time(NULL);
    struct tm* ptm = localtime(&ticks);
    strftime(timeStamp, size, "%Y-%m-%d %H:%M:%S",ptm);
}

std::string Logger::log(Logger::Level level, const char* file, int line, const char* format, ...)
{
    if(m_level > level)
    {
        return "";
    }

    if(m_fout.fail())
    {
        throw std::logic_error("open file failed " + m_fileName);
    }

    //获取当前时间
    char timestamp[32];
    getNowTime(timestamp, sizeof(timestamp));

    //C语言写法
    // const char* fmt = "%s %s %s:%d ";
    // uint32_t size = snprintf(NULL, 0, fmt, timestamp, s_level[level], file, line); //获取字符长度
    // if(size > 0)
    // {
    //     char* buffer = new char[size + 1];
    //     snprintf(buffer, size + 1, fmt, timestamp, s_level[level], file, line);
    //     buffer[size] = 0;
    //     // std::cout << buffer << std::endl;
    //     m_fout << buffer;
    //     delete [] buffer;
    // }

    //C++写法
    std::stringstream oss;
    oss << timestamp << " " << s_level[level] << " " << file << ":" << line << " ";
    // m_fout << oss.str() << std::endl;
    // std::cout << oss.str() << std::endl;

    va_list arg_ptr;
    va_start(arg_ptr, format);
    uint32_t size = vsnprintf(NULL, 0, format, arg_ptr);
    va_end(arg_ptr);
    if(size > 0)
    {
        char* content = new(std::nothrow) char[size + 1];
        if(content == NULL)
        {
            return "";
        }
        va_start(arg_ptr, format);
        vsnprintf(content, size + 1, format, arg_ptr);
        va_end(arg_ptr);
        oss << content << "\n";
        delete [] content;
    }

    // std::cout << oss.str();
    m_fout << oss.str();
    m_fout.flush();

    m_len += oss.str().length();    //更新当前长度
    // std::cout << "m_len = " << m_len << std::endl;
    if(m_len >= m_maxSize)
    {
        // sleep(2);
        _rotaate();
    }

    return oss.str();
}

void Logger::_rotaate()
{
    close();
    char timestamp[32];
    getNowTime(timestamp, sizeof(timestamp));
    std::string fileName = timestamp + m_fileName;

    //删除空格
    auto index = fileName.find(' ');
    if(std::string::npos != index)
    {
        auto it = fileName.begin() + index;
        fileName.erase(it);
    }

    //给之前的日志文件更换名字
    if(std::rename(m_fileName.c_str(), fileName.c_str()) != 0)
    {
        throw std::logic_error("rename log file failed: " + std::string(strerror(errno)));
    }
    open(m_fileName);
}

Logger* Logger::getLogInstance()
{
    if(m_logInstance == nullptr)
    {
        m_logInstance = new Logger();
    }
    return m_logInstance;
}

void Logger::open(const std::string& fileName)
{
    if(m_fout.is_open())
    {
        std::cout << "is open" << std::endl;
        return;
    }
    m_fileName = fileName;
    m_fout.open(fileName, std::ios::app);
    if(m_fout.fail())
    {
        throw std::logic_error("open file failed " + fileName);
    }

    //获取当前日志大小
    m_fout.seekp(0, std::ios::end);
    m_len = m_fout.tellp();
}

void Logger::close()
{
    if(m_fout.is_open())
    {
        m_fout.close();
    }
}